/**
 * 插件作用：处理 vue2 template 链判断运算符（?.）和Null判断运算符（??）
 */

export default () => {
  const babel = require('@babel/core');

  const regex = /\{\{[^\}\}]+\}\}/g;
  const tagsReg = /<([\w]+)\s*[^>]*/g;
  const chainingRegex = /[\w]+(\?\.(\[['"]?)?[\w\-\_]+(['"]?\])?)+/g;

  const WithReplaceComment = '/* __vite-plugin-proposal-coalescing__ */||';
  const matchWithRegex = new RegExp(WithReplaceComment.replace(/[*/|]/g, '\\$&'), 'g');

  function coalescingFormat(code: string, id: string) {
    code = code.replace(matchWithRegex, '??');
    const output = babel.transformSync(code, {
      minified: true,
      plugins: ['@babel/plugin-proposal-nullish-coalescing-operator']
    });
    return output.code;
  }

  function chainingReplace(code: string) {
    code = code.replace(chainingRegex, (m: any) => {
      const [name, ...rest] = m.split('.');
      return `$chaining(${name.replace('?', '')}, '${rest.map((r: any) => r.replace(/[?|\'|"|\[|\]]/g, '')).join('.')}')`;
    });

    return code.replace(/\?\?/g, WithReplaceComment);
  }

  function supportChaining(code: string, id: string) {
    code = code.replace(tagsReg, (match: any, args: any) => {
      return chainingReplace(match);
    });

    code = code.replace(regex, (match: any, args: any) => {
      return chainingReplace(match);
    });

    return code;
  }

  const chainingPlugin = () => {
    return [
      {
        name: 'vite-plugin-chaining',
        enforce: 'pre',
        transform(code: string, id: string) {
          if (!/type=template/.test(id)) {
            return null;
          }
          return supportChaining.call(this, code, id);
        },
        async handleHotUpdate(ctx: any) {
          if (!/\.vue$/.test(ctx.file)) {
            return null;
          }
          const defaultRead = ctx.read;
          ctx.read = async function () {
            return supportChaining.call(this, await defaultRead(), ctx.file);
          };
          return null;
        }
      },
      {
        name: 'vite-plugin-chaining:coalescing',
        enforce: 'post',
        transform(code: string, id: string) {
          if (!/type=template/.test(id)) {
            return null;
          }
          if (code.includes(WithReplaceComment)) {
            return coalescingFormat(code, id);
          }
        }
      }
    ];
  };

  return chainingPlugin();
};
